package com.dxysun.spring.servlet.v5mvc;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.dxysun.annotation.DXYRequestMapping;
import com.dxysun.spring.context.DXYApplicationContext;
import com.dxysun.spring.mvc.DXYHandlerAdapter;
import com.dxysun.spring.mvc.DXYHandlerMapping;
import com.dxysun.spring.mvc.DXYModelAndView;
import com.dxysun.spring.mvc.DXYView;
import com.dxysun.spring.mvc.DXYViewResolver;

public class DxyDispatchServlet extends HttpServlet
{
	private List<DXYHandlerMapping> handlerMappingList = new ArrayList<>();

	private Map<DXYHandlerMapping, DXYHandlerAdapter> handlerAdapterMap = new HashMap<>();

	private Map<String, DXYViewResolver> viewResolverMap = new HashMap<>();

	private DXYApplicationContext applicationContext;

	@Override
	public void init(ServletConfig config) throws ServletException
	{
		super.init(config);

		applicationContext = new DXYApplicationContext(config.getInitParameter("contextConfigLocation"));

		//初始化九大组件
		initStrategies(applicationContext);

		System.out.println("DXY MVC Framework is init");
	}

	private void initStrategies(DXYApplicationContext context)
	{
		//多文件上传的组件
		//        initMultipartResolver(context);
		//初始化本地语言环境
		//        initLocaleResolver(context);
		//初始化模板处理器
		//        initThemeResolver(context);
		//handlerMapping
		initHandlerMappings(context);
		//初始化参数适配器
		initHandlerAdapters(context);
		//初始化异常拦截器
		//        initHandlerExceptionResolvers(context);
		//初始化视图预处理器
		//        initRequestToViewNameTranslator(context);
		//初始化视图转换器
		initViewResolvers(context);
		//FlashMap管理器
		//        initFlashMapManager(context);
	}

	private void initViewResolvers(DXYApplicationContext context)
	{
		String templateRoot = context.getConfig("templateRoot");
		String templateRootPath = this.getClass().getClassLoader().getResource(templateRoot).getFile();

		File templateRootDir = new File(templateRootPath);
		for (File file : templateRootDir.listFiles())
		{
			this.viewResolverMap.put(file.getName(), new DXYViewResolver(templateRoot));
		}

	}

	private void initHandlerAdapters(DXYApplicationContext context)
	{
		for (DXYHandlerMapping handlerMapping : handlerMappingList)
		{
			this.handlerAdapterMap.put(handlerMapping, new DXYHandlerAdapter(handlerMapping));
		}
	}

	private void initHandlerMappings(DXYApplicationContext context)
	{
		doInitHandleMapping(context);
	}

	private void doInitHandleMapping(DXYApplicationContext applicationContext)
	{
		for (String beanName : applicationContext.getBeanDefinitionNames())
		{
			Object object = applicationContext.getBean(beanName);
			if (object == null)
			{
				continue;
			}
			Class<?> clazz = object.getClass();
			if (clazz.isAnnotationPresent(DXYRequestMapping.class))
			{
				DXYRequestMapping dxyRequestMapping = clazz.getAnnotation(DXYRequestMapping.class);
				String baseUrl = dxyRequestMapping.value();
				Method[] methods = clazz.getDeclaredMethods();
				for (Method method : methods)
				{
					if (method.isAnnotationPresent(DXYRequestMapping.class))
					{
						DXYRequestMapping dxyMethodRequestMapping = method.getAnnotation(DXYRequestMapping.class);
						String url = baseUrl + "/" + dxyMethodRequestMapping.value();
						Pattern pattern = Pattern.compile(url.replaceAll("/+", "/"));
						handlerMappingList.add(new DXYHandlerMapping(
								applicationContext.getBean(toFistLowerCase(method.getDeclaringClass().getSimpleName())),
								method, pattern));
						System.out.println("Mapped " + url + "," + method);
					}
				}
			}
		}
	}

	private String toFistLowerCase(String beanName)
	{
		char[] chars = beanName.toCharArray();
		chars[0] += 32;
		return String.valueOf(chars);
	}

	@Override
	public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
	{
		doPost(req, resp);
	}

	@Override
	public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
	{
		try
		{
			doDispatch(request, response);
		}
		catch (Exception e)
		{
			e.printStackTrace();
			response.getWriter().write("500 Exception " + Arrays.toString(e.getStackTrace()));
		}
	}

	public void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception
	{
		String url = request.getRequestURI();
		String contextPath = request.getContextPath();
		url = url.replace(contextPath, "").replaceAll("/+", "/");
		DXYHandlerMapping handler = getHandler(url);
		if (handler == null)
		{
			response.getWriter().write(url + " 404 Not Found!!");
			return;
		}
		DXYHandlerAdapter handlerAdapter = handlerAdapterMap.get(handler);
		DXYModelAndView modelAndView = handlerAdapter.handler(request, response, handler);
		if (modelAndView == null)
			return;
		DXYViewResolver viewResolver = viewResolverMap.get(modelAndView.getViewName());
		DXYView view = viewResolver.resolveViewName(modelAndView.getViewName());
		view.render(modelAndView.getModel(), request, response);

	}

	public DXYHandlerMapping getHandler(String url)
	{
		for (DXYHandlerMapping handler : handlerMappingList)
		{
			if (handler.getPattern().matcher(url).matches())
			{
				return handler;
			}
		}
		return null;
	}

	//url传过来的参数都是String类型的，HTTP是基于字符串协议
	//只需要把String转换为任意类型就好
	private Object convert(Class<?> type, String value)
	{
		if (Integer.class == type)
		{
			return Integer.valueOf(value);
		}
		//如果还有double或者其他类型，继续加if
		//这时候，我们应该想到策略模式了
		//在这里暂时不实现，希望小伙伴自己来实现
		return value;
	}

}
